package com.softserveinc.moviedbapp.manager;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import com.softserveinc.moviedbapp.application.Settings;
import com.softserveinc.moviedbapp.cache.CacheManager;
import com.softserveinc.moviedbapp.fragment.IFragmentCallback;
import com.softserveinc.moviedbapp.fragment.IMovieInfoCallback;
import com.softserveinc.moviedbapp.model.Movie;
import com.softserveinc.moviedbapp.model.MovieInfo;
import com.softserveinc.moviedbapp.util.MovieAppConstants;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class DataManager {

	private Settings mSettings;

	private boolean mIsConnected;

	private IFragmentCallback mMovieItemsFragment;
	private IMovieInfoCallback mMovieInfoFragment;
	private List<Movie> movies;
	private Map<String, List<Movie>> mMoviesSearchMap;
	private MovieInfo mMovieInfo;
	private Context context;
	private CacheManager mCacheManager;
	private String mLastSearchQuery;
	private List<Movie> mWidgetMoviesByRelease;
	private List<Movie> mWidgetMoviesByRating;
	private List<Movie> mWidgetMoviesByTitle;

	private static final String TAG = DataManager.class.getSimpleName();

	public DataManager(final Context ctx, final Settings settings) {
		context = ctx;
		mSettings = settings;
		mCacheManager = new CacheManager(context);
		/* open the database if closed */
		mCacheManager.openDatabase();
	}

	public void provideMovieList(final String searchQuery) {

		if (searchQuery == null) {
			/* no search, just show movie list */
			if (movies == null || movies.isEmpty()) {
				Log.d(TAG, "Going for the movie list, no instance in memory");
				new MovieListRetrieverAsync().execute();
			} else {
				Log.d(TAG, "Returning saved instance of movie list from memory");
				mMovieItemsFragment.onGettingMovieList();
				mMovieItemsFragment.onMovieListRetrieved(movies);
			}
		} else {
			/* search for a title */
			if (mMoviesSearchMap == null) {
				/* searching for the first time */
				Log.d(TAG, "manageMovieList() GOING INTO SEARCH FOR THE FIRST TIME");
				mMoviesSearchMap = new HashMap<String, List<Movie>>();
				new SearchMovieListAsync().execute(searchQuery);
			} else {
				/* searching again */
				Iterator<String> iterator = mMoviesSearchMap.keySet().iterator();
				while (iterator.hasNext()) {
					if (iterator.next().equalsIgnoreCase(searchQuery)) {
						/* search query is in our map */
						Log.d(TAG, "manageMovieList() RETURNING SAVED SEARCH INSTANCE");
						mMovieItemsFragment.onGettingMovieList();
						mMovieItemsFragment.onMovieListRetrieved(mMoviesSearchMap.get(searchQuery));

						break;
					}
					if (!iterator.hasNext()) {
						/* need to download anyway */
						Log.d(TAG, "manageMovieList() GOING INTO NEW SEARCH");
						new SearchMovieListAsync().execute(searchQuery);
					}
				}

			}
		}

	}

	public void provideRefreshedMovieList() {
		new RefreshMovieListAsync().execute();
	}

	public void provideMovieInfo(final int movieId) {

		if (mMovieInfo == null || mMovieInfo.getMovieId() != movieId) {
			Log.d(TAG, "Going for the movie info, no instance in memory movieId = " + movieId);
			new MovieInfoRetrieverAsync().execute(movieId);
		} else {
			Log.d(TAG, "Returning saved instance of movie info from memory");
			mMovieInfoFragment.onGettingMovieInfo();
			mMovieInfoFragment.onMovieInfoRetrieved(mMovieInfo);
			Log.d(TAG, "mMovieInfo urls" + mMovieInfo.getBackdropImagesUrlsList());
		}

	}

	/* launch another async tast and download images from web */
	public void provideNextScreenForMovieInfo(final MovieInfo movieInfo) {

		Log.d(TAG, "GONNA GET SCREENS FOR MOVIE INFO");
		new MovieInfoScreensRetrieverAsync().execute(movieInfo);

	}

	/* gets a movie list ordered by release, rating or title */
	public void provideMovieListForWidget(final String orderBy, final String results) {

		Log.d(TAG, "Going to get a movie list for WIDGET");
		/* get user settings */
		String language = context.getResources().getConfiguration().locale.getLanguage();
		String parserMode = mSettings.getParserMode();

		List<Movie> movies = mCacheManager.getMovieList(language, parserMode, orderBy, results);

		if (orderBy.equals(MovieAppConstants.ORDER_BY_RELEASE)) {
			mWidgetMoviesByRelease = movies;
			Log.d(TAG, "in provideMovieListForWidget() setting mWidgetMoviesByRelease");
		} else if (orderBy.equals(MovieAppConstants.ORDER_BY_RATING)) {
			mWidgetMoviesByRating = movies;
			Log.d(TAG, "in provideMovieListForWidget() setting mWidgetMoviesByRating");
		} else {
			mWidgetMoviesByTitle = movies;
			Log.d(TAG, "in provideMovieListForWidget() setting mWidgetMoviesByTitle");
		}
	}

	public Movie provideMovieForWidget(final String orderBy, final int position) {
		Movie movie = null;
		if (orderBy.equals(MovieAppConstants.ORDER_BY_RELEASE)) {
			movie = mWidgetMoviesByRelease.get(position);
			Log.d(TAG,
					"orderBy = " + MovieAppConstants.ORDER_BY_RELEASE + " and movie for WIDGET is " + movie.toString());
		} else if (orderBy.equals(MovieAppConstants.ORDER_BY_RATING)) {
			movie = mWidgetMoviesByRating.get(position);
			Log.d(TAG, "orderBy = " + MovieAppConstants.ORDER_BY_RATING + " and movie for WIDGET is" + movie.toString());
		} else if (orderBy.equals(MovieAppConstants.ORDER_BY_TITLE)) {
			movie = mWidgetMoviesByTitle.get(position);
			Log.d(TAG, "orderBy = " + MovieAppConstants.ORDER_BY_TITLE + " and movie for WIDGET is" + movie.toString());
		}
		return movie;
	}

	public IFragmentCallback getMovieItems() {
		return mMovieItemsFragment;
	}

	public void setMovieItems(final IFragmentCallback movieItems) {
		this.mMovieItemsFragment = movieItems;
	}

	public IMovieInfoCallback getMovieInfoFragment() {
		return mMovieInfoFragment;
	}

	public void setMovieInfoFragment(final IMovieInfoCallback mMovieInfoFragment) {
		this.mMovieInfoFragment = mMovieInfoFragment;
	}

	public CacheManager getCacheManager() {
		return mCacheManager;
	}

	public void openDatabase() {
		mCacheManager.openDatabase();
	}

	public void closeDatabase() {
		mCacheManager.closeDatabase();
	}

	public boolean isConnected() {
		return mIsConnected;
	}

	public void setIsConnected(final boolean mIsConnected) {
		this.mIsConnected = mIsConnected;
	}

	/* first asynchronous task that retrieves a movie list in a background thread */
	private class MovieListRetrieverAsync extends AsyncTask<Void, Void, List<Movie>> {

		@Override
		protected void onPreExecute() {
			mMovieItemsFragment.onGettingMovieList();
		}

		@Override
		protected List<Movie> doInBackground(final Void... params) {
			/* get user settings */
			String language = context.getResources().getConfiguration().locale.getLanguage();
			Log.d(TAG, "LOCALE IS = " + language);

			String parserMode = mSettings.getParserMode();
			String orderBy = mSettings.getDefaultOrder();
			String results = mSettings.getMoviesDisplayed();
			/* get a list of movies */
			movies = mCacheManager.getMovieList(language, parserMode, orderBy, results);
			return movies;
		}

		/* receives ArrayList<Movie> from doInBackground() */
		@Override
		protected void onPostExecute(final List<Movie> result) {
			if (result.isEmpty()) {
				mMovieItemsFragment.onMovieListRetrievalFailed();
			} else {
				mMovieItemsFragment.onMovieListRetrieved(result);
			}
		}
	}

	/* second asynchronous task that retrieves a movie information in a background thread */
	private class MovieInfoRetrieverAsync extends AsyncTask<Integer, Void, MovieInfo> {

		@Override
		protected void onPreExecute() {
			mMovieInfoFragment.onGettingMovieInfo();
		}

		@Override
		protected MovieInfo doInBackground(final Integer... params) {
			/* get user settings */
			String language = context.getResources().getConfiguration().locale.getLanguage();
			String parserMode = mSettings.getParserMode();

			int movieId = params[0];
			Log.d(TAG, "inside doInBackground(final Integer... params)\n and movieId = " + movieId);
			/* get a list of movies */
			mMovieInfo = mCacheManager.getMovieInfo(language, parserMode, movieId);

			return mMovieInfo;
		}

		@Override
		protected void onPostExecute(final MovieInfo result) {
			if (result == null) {
				mMovieInfoFragment.onMovieInfoRetrievalFailed();
			} else {
				mMovieInfoFragment.onMovieInfoRetrieved(result);
			}
		}
	}

	/* third asynchronous task that searches for a movie title in a background thread */
	private class SearchMovieListAsync extends AsyncTask<String, Void, Map<String, List<Movie>>> {

		@Override
		protected void onPreExecute() {
			mMovieItemsFragment.onGettingMovieList();
		}

		@Override
		protected Map<String, List<Movie>> doInBackground(final String... params) {
			/* get user settings */
			mLastSearchQuery = params[0];
			String language = context.getResources().getConfiguration().locale.getLanguage();
			String parserMode = mSettings.getParserMode();
			Log.d(TAG, "inside doInBackground(final String... params)\n and Search query IS " + mLastSearchQuery);
			mMoviesSearchMap
					.put(mLastSearchQuery, mCacheManager.searchForMovie(language, parserMode, mLastSearchQuery));
			return mMoviesSearchMap;
		}

		/* receives ArrayList<Movie> from doInBackground() */
		@Override
		protected void onPostExecute(final Map<String, List<Movie>> result) {
			if (result.isEmpty()) {
				mMovieItemsFragment.onMovieListRetrievalFailed();
			} else {
				mMovieItemsFragment.onMovieListRetrieved(result.get(mLastSearchQuery));
			}
		}

	}

	/* fourth asynchronous task that refreshes the list of movies */
	private class RefreshMovieListAsync extends AsyncTask<Void, Void, List<Movie>> {

		@Override
		protected void onPreExecute() {
			mMovieItemsFragment.onGettingMovieList();
		}

		@Override
		protected List<Movie> doInBackground(final Void... params) {
			/* get user settings */
			String language = context.getResources().getConfiguration().locale.getLanguage();
			String parserMode = mSettings.getParserMode();
			String orderBy = mSettings.getDefaultOrder();
			String results = mSettings.getMoviesDisplayed();
			Log.d(TAG, "inside doInBackground(final Void... params)\n and REFRESHING ");
			movies = mCacheManager.getRefreshedMovieList(language, parserMode, orderBy, results);
			return movies;
		}

		/* receives ArrayList<Movie> from doInBackground() */
		@Override
		protected void onPostExecute(final List<Movie> result) {
			if (result.isEmpty()) {
				mMovieItemsFragment.onMovieListRetrievalFailed();
			} else {
				mMovieItemsFragment.onMovieListRetrieved(result);
			}
		}

	}

	/* fifth asynchronous task that retrieves a movie screens in a background thread */
	private class MovieInfoScreensRetrieverAsync extends AsyncTask<MovieInfo, Void, MovieInfo> {

		@Override
		protected MovieInfo doInBackground(final MovieInfo... paramArrayOfParams) {

			return mCacheManager.getMovieScreen(paramArrayOfParams[0]);
		}

		@Override
		protected void onPostExecute(final MovieInfo result) {
			Log.d(TAG, "onPostExecute(final MovieInfo result)");
			if (result == null) {
				mMovieInfoFragment.onMovieInfoRetrievalFailed();
			} else {
				mMovieInfoFragment.onMovieInfoRetrieved(result);
			}
		}

	}

}
